package com.autumn.platform.core.logger;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ClassUtils;

import com.autumn.platform.core.context.track.Tracker;
import com.autumn.platform.core.logger.impl.JdkLevelUpdater;
import com.autumn.platform.core.logger.impl.Log4j2LevelUpdater;
import com.autumn.platform.core.logger.impl.Log4jLevelUpdater;
import com.autumn.platform.core.logger.impl.LogbackLevelUpdater;

public abstract class Logs {

    private Logs() {};

    private enum LogLevel {
        TRACE, DEBUG, INFO, WARN, ERROR
    };

    private static final Map<String, Logger> loggerCache = new ConcurrentHashMap<String, Logger>(16);

    private static LogLevelUpdater logLevelUpdater;

    /**
     * 获取日志类实例
     * 
     * @param cls
     * @return
     */
    public static Logger getLogger(Class<?> cls) {
        return getLogger(cls.getName());
    }

    /**
     * 获取日志类实例
     * 
     * @param name
     * @return
     */
    public static Logger getLogger(String name) {
        Logger logger = loggerCache.get(name);
        if (null == logger) {
            synchronized (loggerCache) {
                logger = loggerCache.get(name);
                if (null == logger) {
                    logger = LoggerFactory.getLogger(name);
                    loggerCache.put(name, logger);
                }
            }
        }
        return logger;
    }

    /**
     * 获取日志更新器
     * 
     * @return
     */
    public static LogLevelUpdater getLogLevelUpdater() {
        if (null == logLevelUpdater) {
            synchronized (Logs.class) {
                if (null == logLevelUpdater) {
                    try {
                        ClassLoader classLoader = Logs.class.getClassLoader();
                        if (ClassUtils.isPresent("ch.qos.logback.classic.Logger", classLoader)) {
                            logLevelUpdater = new LogbackLevelUpdater();
                        } else if (ClassUtils.isPresent("org.apache.logging.log4j.core.Logger", classLoader)) {
                            logLevelUpdater = new Log4j2LevelUpdater();
                        } else if (ClassUtils.isPresent("org.apache.log4j.Logger", classLoader)) {
                            logLevelUpdater = new Log4jLevelUpdater();
                        } else if (ClassUtils.isPresent("java.util.logging.Logger", classLoader)) {
                            logLevelUpdater = new JdkLevelUpdater();
                        } else {
                            logLevelUpdater = new JdkLevelUpdater();
                        }
                    } catch (Exception e) {
                        //ignore 
                    }
                }
            }
        }
        return logLevelUpdater;
    }

    /**
     * 是否已开启TRACE模式
     * 
     * @return 是否已开启
     */
    public static boolean isTraceEnabled() {
        StackTraceElement stack = getStack();
        return isEnabled(stack, LogLevel.TRACE);
    }

    /**
     * 是否已开启DEBUG模式
     * 
     * @return 是否已开启
     */
    public static boolean isDebugEnabled() {
        StackTraceElement stack = getStack();
        return isEnabled(stack, LogLevel.DEBUG);
    }

    /**
     * 是否已开启INFO模式
     * 
     * @return 是否已开启
     */
    public static boolean isInfoEnabled() {
        StackTraceElement stack = getStack();
        return isEnabled(stack, LogLevel.INFO);
    }

    /**
     * 是否已开启WARN模式
     * 
     * @return 是否已开启
     */
    public static boolean isWarnEnabled() {
        StackTraceElement stack = getStack();
        return isEnabled(stack, LogLevel.WARN);
    }

    /**
     * 是否已开启ERROR模式
     * 
     * @return 是否已开启
     */
    public static boolean isErrorEnabled() {
        StackTraceElement stack = getStack();
        return isEnabled(stack, LogLevel.ERROR);
    }

    /**
     * 打印TRACE级别日志
     * 
     * @param msg 日志内容
     * @param args 消息占位符参数
     */
    public static void trace(String msg) {
        StackTraceElement stack = getStack();
        write(LogLevel.TRACE, stack, msg, null);
    }

    /**
     * 打印TRACE级别日志
     * 
     * @param e 异常
     */
    public static void trace(Throwable e) {
        StackTraceElement stack = getStack();
        write(LogLevel.TRACE, stack, null, e);
    }

    /**
     * 打印TRACE级别日志
     * 
     * @param msg 日志内容
     * @param e 异常
     * @param args 消息占位符参数
     */
    public static void trace(String msg, Throwable e) {
        StackTraceElement stack = getStack();
        write(LogLevel.TRACE, stack, msg, e);
    }

    /**
     * 打印DEBUG级别日志
     * 
     * @param msg 日志内容
     * @param args 消息占位符参数
     */
    public static void debug(String msg) {
        StackTraceElement stack = getStack();
        write(LogLevel.DEBUG, stack, msg, null);
    }

    /**
     * 打印DEBUG级别日志
     * 
     * @param e 异常
     */
    public static void debug(Throwable e) {
        StackTraceElement stack = getStack();
        write(LogLevel.DEBUG, stack, null, e);
    }

    /**
     * 打印DEBUG级别日志
     * 
     * @param msg 日志内容
     * @param e 异常
     * @param args 消息占位符参数
     */
    public static void debug(String msg, Throwable e) {
        StackTraceElement stack = getStack();
        write(LogLevel.DEBUG, stack, msg, e);
    }

    /**
     * 打印INFO级别日志
     * 
     * @param msg 日志内容
     * @param args 消息占位符参数
     */
    public static void info(String msg) {
        StackTraceElement stack = getStack();
        write(LogLevel.INFO, stack, msg, null);
    }

    /**
     * 打印INFO级别日志
     * 
     * @param e 异常
     */
    public static void info(Throwable e) {
        StackTraceElement stack = getStack();
        write(LogLevel.INFO, stack, null, e);
    }

    /**
     * 打印INFO级别日志
     * 
     * @param msg 日志内容
     * @param e 异常
     * @param args 消息占位符参数
     */
    public static void info(String msg, Throwable e) {
        StackTraceElement stack = getStack();
        write(LogLevel.INFO, stack, msg, e);
    }

    /**
     * 打印WARN级别日志
     * 
     * @param msg 日志内容
     * @param args 消息占位符参数
     */
    public static void warn(String msg) {
        StackTraceElement stack = getStack();
        write(LogLevel.WARN, stack, msg, null);
    }

    /**
     * 打印WARN级别日志
     * 
     * @param e 异常
     */
    public static void warn(Throwable e) {
        StackTraceElement stack = getStack();
        write(LogLevel.WARN, stack, null, e);
    }

    /**
     * 打印WARN级别日志
     * 
     * @param msg 日志内容
     * @param e 异常
     * @param args 消息占位符参数
     */
    public static void warn(String msg, Throwable e) {
        StackTraceElement stack = getStack();
        write(LogLevel.WARN, stack, msg, e);
    }

    /**
     * 打印ERROR级别日志
     * 
     * @param msg 日志内容
     */
    public static void error(String msg) {
        StackTraceElement stack = getStack();
        write(LogLevel.ERROR, stack, msg, null);
    }

    /**
     * 打印ERROR级别日志
     * 
     * @param e 异常
     */
    public static void error(Throwable e) {
        StackTraceElement stack = getStack();
        write(LogLevel.ERROR, stack, null, e);
    }

    /**
     * 打印ERROR级别日志
     * 
     * @param msg 日志内容
     * @param e 异常
     */
    public static void error(String msg, Throwable e) {
        StackTraceElement stack = getStack();
        write(LogLevel.ERROR, stack, msg, e);
    }

    /**
     * 是否已开启日志级别
     * 
     * @param stack 日志堆栈
     * @param logLevel 日志级别
     * @return 是否已开启
     */
    private static boolean isEnabled(StackTraceElement stack, LogLevel logLevel) {
        Logger logger = LoggerFactory.getLogger(stack.getClassName());
        boolean enabled = false;
        switch (logLevel) {
            case TRACE:
                enabled = logger.isTraceEnabled();
                break;
            case DEBUG:
                enabled = logger.isDebugEnabled();
                break;
            case INFO:
                enabled = logger.isInfoEnabled();
                break;
            case WARN:
                enabled = logger.isWarnEnabled();
                break;
            case ERROR:
                enabled = logger.isErrorEnabled();
                break;
        }
        return enabled;
    }

    /**
     * 写日志
     * 
     * @param logLevel 日志级别
     * @param stack 日志堆栈
     * @param message 日志内容
     * @param e 异常
     */
    private static void write(LogLevel logLevel, StackTraceElement stack, String message, Throwable e) {
        message = Tracker.appendTrackId(message);
        Logger logger = LoggerFactory.getLogger(stack.getClassName());
        switch (logLevel) {
            case TRACE:
                if (logger.isTraceEnabled()) {
                    logger.trace(message, e);
                }
                break;
            case DEBUG:
                if (logger.isDebugEnabled()) {
                    logger.debug(message, e);
                }
                break;
            case INFO:
                if (logger.isInfoEnabled()) {
                    logger.info(message, e);
                }
                break;
            case WARN:
                if (logger.isWarnEnabled()) {
                    logger.warn(message, e);
                }
                break;
            case ERROR:
                if (logger.isErrorEnabled()) {
                    logger.error(message, e);
                }
                break;
        }
    }

    /**
     * 获取堆栈
     * 
     * @return
     */
    private static StackTraceElement getStack() {
        return new Exception().getStackTrace()[2];
    }
}
